package cn.iocoder.yudao.module.wms.service.inventory;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.InventoryRecordInOutReqVO;
import cn.iocoder.yudao.module.wms.dal.dataobject.commodity.CommodityDO;
import cn.iocoder.yudao.module.wms.dal.dataobject.inventoryrecord.InventoryRecordDO;
import cn.iocoder.yudao.module.wms.dal.dataobject.warehouse.WarehouseDO;
import cn.iocoder.yudao.module.wms.service.commodity.CommodityService;
import cn.iocoder.yudao.module.wms.service.inventoryrecord.InventoryRecordService;
import cn.iocoder.yudao.module.wms.service.warehouse.WarehouseService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.*;
import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.InventoryDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.wms.dal.mysql.inventory.InventoryMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*;

/**
 * 库存 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class InventoryServiceImpl extends ServiceImpl<InventoryMapper, InventoryDO> implements InventoryService {

    @Resource
    private InventoryMapper inventoryMapper;
    @Resource
    private CommodityService commodityService;
    @Resource
    private WarehouseService warehouseService;
    @Resource
    private InventoryRecordService inventoryRecordService;

    @Override
    public String createInventory(InventorySaveReqVO createReqVO) {
        // 插入
        InventoryDO inventory = BeanUtils.toBean(createReqVO, InventoryDO.class);
        String now = LocalDateTimeUtil.formatNormal(LocalDateTime.now());
        inventory.setId(UUID.randomUUID().toString())
                .setCreateAt(now)
                .setUpdateAt(now);
        inventoryMapper.insert(inventory);
        // 返回
        return inventory.getId();
    }

    @Override
    public String saveInventory(InventoryDO inventoryDO) {
        inventoryMapper.insert(inventoryDO);
        return inventoryDO.getId();
    }


    @Override
    public void updateInventory(InventorySaveReqVO updateReqVO) {
        // 校验存在
        validateInventoryExists(updateReqVO.getId());
        // 更新
        InventoryDO updateObj = BeanUtils.toBean(updateReqVO, InventoryDO.class);
        updateObj.setUpdateAt(LocalDateTimeUtil.formatNormal(LocalDateTime.now()));
        inventoryMapper.updateById(updateObj);
    }

    @Override
    public void updateInventory(InventoryDO inventoryDO) {
        inventoryMapper.updateById(inventoryDO);
    }


    @Override
    public void deleteInventory(String id) {
        // 校验存在
        validateInventoryExists(id);
        // 删除
        inventoryMapper.deleteById(id);
    }

    private void validateInventoryExists(String id) {
        if (inventoryMapper.selectById(id) == null) {
            throw exception(INVENTORY_NOT_EXISTS);
        }
    }

    @Override
    public InventoryDO getInventory(String id) {
        return inventoryMapper.selectById(id);
    }

    @Override
    public InventoryDO getInventoryByWarehouseId(String warehouseId) {
        return inventoryMapper.selectOne(
                Wrappers.lambdaQuery(InventoryDO.class)
                        .eq(InventoryDO::getWid, warehouseId)
        );
    }

    @Override
    public PageResult<InventoryDO> getInventoryPage(InventoryPageReqVO pageReqVO) {
        return inventoryMapper.selectPage(pageReqVO);
    }

    @Override
    public InventoryDO getInventoryByCidAndWid(String cid, String wid) {
        return inventoryMapper.selectOne(
                Wrappers.lambdaQuery(InventoryDO.class)
                        .eq(InventoryDO::getCid, cid)
                        .eq(InventoryDO::getWid, wid)
        );
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String in(InventoryRecordInOutReqVO reqVO) {
        // 查询仓库是否存在
        WarehouseDO warehouseDO = warehouseService.getWarehouse(reqVO.getWid());
        if (warehouseDO == null) {
            throw exception(WAREHOUSE_NOT_EXISTS);
        }

        // 查询商品是否存在，然后入库
        CommodityDO commodityDO = commodityService.getCommodity(reqVO.getCid());
        if (commodityDO == null) {
            throw exception(COMMODITY_NOT_EXISTS);
        }
        commodityDO
                .setCount(commodityDO.getCount() + reqVO.getCount())
                .setUpdateAt(LocalDateTimeUtil.formatNormal(LocalDateTime.now()));
        commodityService.updateById(commodityDO);

        // 查询商品是否在该仓库的库存，然后入库
        InventoryDO inventoryDO = this.getInventoryByCidAndWid(reqVO.getCid(), reqVO.getWid());
        if (inventoryDO == null) {
            String now = LocalDateTimeUtil.formatNormal(LocalDateTime.now());
            // 如果不存在，则创建库存
            inventoryDO = new InventoryDO()
                    .setId(UUID.randomUUID().toString())
                    .setCid(reqVO.getCid())
                    .setCount(reqVO.getCount())
                    .setLocation(null)
                    .setName(commodityDO.getName())
                    .setWid(reqVO.getWid())
                    .setCreateAt(now)
                    .setUpdateAt(now);
            this.save(inventoryDO);
        } else {
            // 如果存在，则更新库存
            inventoryDO.setCount(inventoryDO.getCount() + reqVO.getCount())
                    .setUpdateAt(LocalDateTimeUtil.formatNormal(LocalDateTime.now()));
            this.updateById(inventoryDO);
        }

        // 保存入库记录
        InventoryRecordDO inventoryRecordDO = BeanUtils.toBean(reqVO, InventoryRecordDO.class);
        String now = LocalDateTimeUtil.formatNormal(LocalDateTime.now());
        inventoryRecordDO
                .setId(UUID.randomUUID().toString())
                .setName(commodityDO.getName())
                .setType(+1)
                .setCreateAt(now)
                .setUpdateAt(now);
        inventoryRecordService.save(inventoryRecordDO);
        return inventoryRecordDO.getId();
    }

    @Transactional(rollbackFor = Exception.class) // 添加事务注解，保证全部回滚，否则会有事务问题：当调用多个Service操作时，有可能出现这样的情况，如果中间Service方法异常，只有该方法及以下的方法能回滚，该方法以上的方法不能回滚，导致数据不一致问题。
    @Override
    public String out(InventoryRecordInOutReqVO reqVO) {
        // 查询当前商品在该仓库的库存
        InventoryDO inventoryDO = this.getInventoryByCidAndWid(reqVO.getCid(), reqVO.getWid());
        // 查询结果为空
        if (inventoryDO == null) {
            throw exception(INVENTORY_COMMODITY_NOT_EXISTS);
        }
        // 比较库存
        if (inventoryDO.getCount() < reqVO.getCount()) {
            throw exception(INVENTORY_COUNT_NOT_ENOUGH);
        }

        // 获取商品信息
        CommodityDO commodityDO = commodityService.getCommodity(reqVO.getCid());
        if (commodityDO == null) {
            throw exception(COMMODITY_NOT_EXISTS);
        }
        // 更新商品数量
        commodityDO.setCount(commodityDO.getCount() - reqVO.getCount())
                .setUpdateAt(LocalDateTimeUtil.formatNormal(LocalDateTime.now()));
        commodityService.updateById(commodityDO);

        // 更新库存数量
        inventoryDO.setCount(inventoryDO.getCount() - reqVO.getCount())
                .setUpdateAt(LocalDateTimeUtil.formatNormal(LocalDateTime.now()));
        this.updateById(inventoryDO);

        // 保存库存记录
        InventoryRecordDO inventoryRecordDO = BeanUtils.toBean(reqVO, InventoryRecordDO.class);
        String now = LocalDateTimeUtil.formatNormal(LocalDateTime.now());
        inventoryRecordDO
                .setId(UUID.randomUUID().toString())
                .setName(commodityDO.getName())
                .setType(-1)
                .setCreateAt(now)
                .setUpdateAt(now);
        inventoryRecordService.save(inventoryRecordDO);
        return inventoryRecordDO.getId();
    }
}